17 A Arquitetura do Bloco Transformer
O “Bloco Transformer” é a unidade atômica fundamental de arquiteturas baseadas em Large Language Models (LLMs). Enquanto o mecanismo de Self-Attention (discutido em capítulos anteriores) é responsável por contextualizar tokens entre si, o Bloco Transformer é a estrutura que encapsula esse mecanismo, processa a informação extraída e estabiliza o fluxo de gradientes através da rede profunda.
Um modelo moderno (como GPT-4, LLaMA ou Claude) é essencialmente uma pilha vertical de dezenas ou centenas desses blocos idênticos.
17.1 1. Visão Geral da Arquitetura
A arquitetura moderna do bloco divergiu ligeiramente do paper original “Attention is All You Need” (2017). A mudança mais significativa é a adoção da configuração Pre-Norm (normalização antes das subcamadas) em vez de Post-Norm, para garantir estabilidade no treinamento de modelos profundos.
O fluxo de dados dentro de um único bloco segue esta ordem lógica: 1. Entrada (\(x\)) 2. Normalização 1 3. Self-Attention 4. Conexão Residual 1 (Soma com a entrada) 5. Normalização 2 6. Feed-Forward Network (FFN) 7. Conexão Residual 2 (Soma com o resultado anterior)
17.1.1 Diagrama de Fluxo de Dados (Pre-Norm)
graph TD
Input[Entrada do Bloco <br/> Tensor: Batch, Seq, Dim] --> Split1(( ))
%% Caminho Residual 1
Split1 -->|Caminho Residual| Add1((Soma))
%% Caminho de Atenção
Split1 --> Norm1[RMSNorm / LayerNorm]
Norm1 --> Attn[Multi-Head Self-Attention]
Attn --> Add1
%% Caminho Residual 2
Add1 --> Split2(( ))
Split2 -->|Caminho Residual| Add2((Soma))
%% Caminho FFN
Split2 --> Norm2[RMSNorm / LayerNorm]
Norm2 --> FFN[Feed-Forward Network <br/> Projeção -> Ativação -> Projeção]
FFN --> Add2
Add2 --> Output[Saída do Bloco]
style Input fill:#f9f,stroke:#333,stroke-width:2px
style Output fill:#f9f,stroke:#333,stroke-width:2px
style Add1 fill:#bbf,stroke:#333
style Add2 fill:#bbf,stroke:#333
17.2 2. Conexões Residuais (Skip Connections)
As conexões residuais são vitais para o treinamento de redes neurais profundas. Matematicamente, se considerarmos uma subcamada (como a Atenção) como uma função \(F(x)\), a saída do bloco não é apenas \(F(x)\), mas sim:
\[ Output = x + F(x) \]
17.2.1 Função Técnica
- Mitigação do Desvanecimento de Gradiente (Vanishing Gradient): Durante a retropropagação (backpropagation), o gradiente pode fluir diretamente através da conexão “skip” (\(x\)) sem ser atenuado pelas operações complexas da função \(F(x)\). Isso cria uma “superestrada” para o gradiente fluir das últimas camadas até as primeiras.
- Preservação de Informação: Permite que o modelo retenha informações da entrada original se a transformação \(F(x)\) não for necessária ou for destrutiva naquele estágio específico.
17.3 3. Normalização: LayerNorm vs. RMSNorm
A normalização é responsável por manter a estabilidade numérica dos valores dos neurônios (ativações), garantindo que eles não explodam nem desapareçam.
17.3.1 Layer Normalization (LayerNorm)
A abordagem clássica. Para um vetor de entrada \(x\), a LayerNorm calcula a média \(\mu\) e a variância \(\sigma^2\) de todas as dimensões daquele vetor específico (independente do batch).
\[ \text{LayerNorm}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \cdot \gamma + \beta \]
Onde \(\gamma\) e \(\beta\) são parâmetros aprendíveis de escala e deslocamento (bias).
17.3.2 Root Mean Square Normalization (RMSNorm)
Adotada por modelos modernos como LLaMA, Gopher e T5. A RMSNorm baseia-se na hipótese de que a recentralização (subtrair a média \(\mu\)) não é fundamental para a convergência, apenas a reescala.
\[ \text{RMSNorm}(x) = \frac{x}{\sqrt{\text{RMS}(x)^2 + \epsilon}} \cdot \gamma \]
\[ \text{RMS}(x) = \sqrt{\frac{1}{d} \sum_{i=1}^{d} x_i^2} \]
Vantagens da RMSNorm: * Eficiência Computacional: Remove o cálculo da média, economizando operações, o que é significativo em modelos com bilhões de parâmetros. * Simplicidade: Mantém a invariância de escala sem a complexidade do deslocamento de média.
17.4 4. Camadas Feed-Forward (FFN)
Enquanto a Atenção agrega informações de diferentes tokens (mistura temporal/contextual), a FFN processa essas informações para cada token individualmente (processamento de características). É frequentemente descrita como a “memória associativa” do modelo.
17.4.1 Estrutura Clássica (MLP)
Uma FFN padrão consiste em duas transformações lineares com uma função de ativação não-linear no meio. Geralmente, ela expande a dimensão oculta (frequentemente por um fator de 4) e depois a projeta de volta.
\[ \text{FFN}(x) = \text{Ativação}(x W_1 + b_1) W_2 + b_2 \]
17.4.2 Estrutura Moderna (SwiGLU / Gated Linear Units)
Modelos de ponta (como PaLM e LLaMA) utilizam variantes “Gated” (com portas), especificamente a SwiGLU.
Nesta configuração, existem três matrizes de peso em vez de duas, e a ativação funciona como um portão lógico suave:
\[ \text{SwiGLU}(x) = (\text{Swish}(x W_{gate}) \otimes (x W_{in})) W_{out} \]
- \(W_{in}\): Projeta para a dimensão expandida.
- \(W_{gate}\): Cria uma “máscara” de ativação.
- \(\otimes\): Multiplicação elemento a elemento (Hadamard product).
- \(W_{out}\): Projeta de volta para a dimensão do modelo.
Isso permite um controle mais refinado sobre quais informações fluem através da rede.
17.5 5. Implementação de Referência (PyTorch)
Abaixo, uma implementação de um Bloco Transformer moderno utilizando Pre-Norm, RMSNorm e uma estrutura MLP padrão para clareza.
import torch
import torch.nn as nn
import torch.nn.functional as F
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.eps = eps
# Parâmetro de escala aprendível (gamma)
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
# Cálculo do Root Mean Square
var = torch.mean(x ** 2, dim=-1, keepdim=True)
x_norm = x * torch.rsqrt(var + self.eps)
return self.weight * x_norm
class FeedForward(nn.Module):
def __init__(self, dim: int, hidden_dim: int, dropout: float = 0.0):
super().__init__()
self.net = nn.Sequential(
nn.Linear(dim, hidden_dim),
nn.GELU(), # Ativação moderna (Gaussian Error Linear Unit)
nn.Linear(hidden_dim, dim),
nn.Dropout(dropout)
)
def forward(self, x):
return self.net(x)
class TransformerBlock(nn.Module):
def __init__(self, dim: int, num_heads: int, mlp_ratio: float = 4.0, dropout: float = 0.1):
super().__init__()
self.norm1 = RMSNorm(dim)
self.attn = nn.MultiheadAttention(embed_dim=dim, num_heads=num_heads, batch_first=True)
self.norm2 = RMSNorm(dim)
hidden_dim = int(dim * mlp_ratio)
self.ffn = FeedForward(dim, hidden_dim, dropout)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
# 1. Pre-Norm + Atenção + Residual
# Nota: A entrada 'x' é preservada para a soma (skip connection)
norm_x = self.norm1(x)
attn_out, _ = self.attn(norm_x, norm_x, norm_x)
x = x + self.dropout(attn_out)
# 2. Pre-Norm + FFN + Residual
# Nota: O 'x' atualizado é preservado novamente
norm_x = self.norm2(x)
ffn_out = self.ffn(norm_x)
x = x + self.dropout(ffn_out)
return x
# Exemplo de instanciação
# Dimensão do modelo: 512, Cabeças: 8
block = TransformerBlock(dim=512, num_heads=8)
dummy_input = torch.randn(1, 10, 512) # Batch=1, Seq=10, Dim=512
output = block(dummy_input)
print(f"Shape de saída: {output.shape}")17.6 Resumo do Capítulo
A eficácia do Bloco Transformer reside na orquestração cuidadosa de seus componentes: 1. RMSNorm (Pre-Norm): Estabiliza a entrada antes do processamento pesado. 2. Atenção: Mistura informações entre tokens. 3. FFN: Processa e “raciocina” sobre as informações misturadas, expandindo a dimensionalidade para capturar relações não-lineares complexas. 4. Residuais: Garantem que o sinal original nunca seja perdido e que os gradientes fluam saudavelmente durante o treinamento.